/**
 * An Example of implementing a CSV Grammar with Chevrotain.
 *
 * Based on: https://github.com/antlr/grammars-v4/blob/master/csv/CSV.g4
 *
 * Note that this is a pure grammar without any actions (either embedded or via a CST Visitor).
 */
import { createToken, Lexer, CstParser, EMPTY_ALT } from "chevrotain";

// ----------------- lexer -----------------
const Text = createToken({ name: "Text", pattern: /[^,\n\r"]+/ });
const Comma = createToken({ name: "Comma", pattern: /,/ });
const NewLine = createToken({
  name: "NewLine",
  pattern: /\r?\n/,
});
const String = createToken({ name: "String", pattern: /"(?:""|[^"])*"/ });

const allTokens = [Text, String, Comma, NewLine];
const CsvLexer = new Lexer(allTokens);

// Parser
class CsvParser extends CstParser {
  constructor() {
    super(allTokens);

    // not mandatory, using $ (or any other sign) to reduce verbosity
    const $ = this;

    $.RULE("csvFile", () => {
      $.SUBRULE($.hdr);
      $.AT_LEAST_ONE(() => {
        $.SUBRULE2($.row);
      });
    });

    $.RULE("hdr", () => {
      $.SUBRULE($.row);
    });

    $.RULE("row", () => {
      $.SUBRULE($.field);
      $.MANY(() => {
        $.CONSUME(Comma);
        $.SUBRULE2($.field);
      });
      $.CONSUME(NewLine);
    });

    $.RULE("field", () => {
      $.OR([
        { ALT: () => $.CONSUME(Text) },
        { ALT: () => $.CONSUME(String) },
        { ALT: EMPTY_ALT("empty field") },
      ]);
    });

    // very important to call this after all the rules have been defined.
    // otherwise the parser may not work correctly as it will lack information
    // derived during the self analysis phase.
    this.performSelfAnalysis();
  }
}

// wrapping it all together
// reuse the same parser instance.
const parser = new CsvParser([]);

export function parseCsv(text) {
  // 1. Tokenize the input.
  const lexResult = CsvLexer.tokenize(text);

  // 2. Set the Parser's input
  parser.input = lexResult.tokens;

  // 3. invoke the desired parser rule
  const cst = parser.csvFile();

  return {
    cst: cst,
    lexResult: lexResult,
    parseErrors: parser.errors,
  };
}
